
let Chart = function () {
    let _gl
    let _config

    let _pointShaderProgram
    let _pointPositionAttributeLocation
    let _pointSizeAttributeLocation
    let _pointFragColor

    let _lineShaderProgram
    let _linePositionAttributeLocation
    let _lineFragColor

    let points = []
    let pointSizes = []
    let linePoints = []

    let xingzuoCirclePoints1 = []
    let xingzuoCirclePoints2 = []

    // 绘制点的顶点着色器代码
    let pointVertexShaderSource = `
      attribute vec4 a_position;
      attribute float a_PointSize;
      void main() {
          gl_PointSize= a_PointSize;
          gl_Position = a_position;
      }
    `
    // 绘制点的片原着色器代码
    let pointFragmentShaderSource = `
        precision mediump float;
        uniform vec4 u_FragColor;
        void main() {
            float d = distance(gl_PointCoord,vec2(0.5,0.5));
            if(d < 0.5){
              gl_FragColor = u_FragColor;
            }else{
                discard;
            }
        }
    `

    // 绘制线的顶点着色器代码
    let lineVertexShaderSource = `
      attribute vec4 a_position;
      void main() {
          gl_PointSize=100.0;
          gl_Position = a_position;
      }
    `
    // 绘制线的片原着色器代码
    let lineFragmentShaderSource = `
        precision mediump float;
        uniform vec4 u_FragColor;
        void main() {
          gl_FragColor = u_FragColor;
        }
    `

    function _initGl() {
        // 环境上下文准备
        _prepareContext()
        // 编译
        let pointVertexShader = _setupShader(_gl, _gl.VERTEX_SHADER, pointVertexShaderSource)
        let pointFragmentShader = _setupShader(_gl, _gl.FRAGMENT_SHADER, pointFragmentShaderSource)

        // 链接
        _pointShaderProgram = linkShader(_gl, pointVertexShader, pointFragmentShader)
        _pointPositionAttributeLocation = _gl.getAttribLocation(_pointShaderProgram, 'a_position')
        _pointSizeAttributeLocation = _gl.getAttribLocation(_pointShaderProgram, 'a_PointSize')

        // 获取u_FragColor变量的存储位置
        _pointFragColor = _gl.getUniformLocation(_pointShaderProgram, 'u_FragColor')

        // 编译
        let lineVertexShader = _setupShader(_gl, _gl.VERTEX_SHADER, lineVertexShaderSource)
        let lineFragmentShader = _setupShader(_gl, _gl.FRAGMENT_SHADER, lineFragmentShaderSource)

        // 链接
        _lineShaderProgram = linkShader(_gl, lineVertexShader, lineFragmentShader)
        _linePositionAttributeLocation = _gl.getAttribLocation(_lineShaderProgram, 'a_position')

        // 获取u_FragColor变量的存储位置
        _lineFragColor = _gl.getUniformLocation(_lineShaderProgram, 'u_FragColor')

        // 准备画布
        _gl.viewport(0, 0, _gl.canvas.width, _gl.canvas.height)
        _gl.clearColor(_config.r, _config.g, _config.b, 1)
        _gl.clear(_gl.COLOR_BUFFER_BIT)
    }

    function _drawLine(x1, y1, x2, y2) {
        // 准备输入数据
        _createBuffer([x1, y1,
            x2, y2])
        // 加载着色器，准备读取缓冲区的数据
        _gl.useProgram(_lineShaderProgram)
        _gl.enableVertexAttribArray(_linePositionAttributeLocation)
        // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
        // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
        // type=gl_FLOAT表示使用的是32为浮点类型的数据。
        // normalize=false表示不需要归一化数据。
        // offset=0表示从缓冲区的开始位置读取数据。
        _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

        _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)
        _gl.drawArrays(_gl.LINES, 0, 2)
    }

    function _drawCrossLine() {
        // 绘制十字线
        // 准备输入数据
        _createBuffer([-1, 0,
            1, 0,
            0, 1,
            0, -1])
        // 加载着色器，准备读取缓冲区的数据
        _gl.useProgram(_lineShaderProgram)
        _gl.enableVertexAttribArray(_linePositionAttributeLocation)
        // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
        // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
        // type=gl_FLOAT表示使用的是32为浮点类型的数据。
        // normalize=false表示不需要归一化数据。
        // offset=0表示从缓冲区的开始位置读取数据。
        _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

        _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)
        _gl.drawArrays(_gl.LINES, 0, 4)
    }

    /**
     * 绘制一个点
     */
    function _drawPoint(x, y, size, r, g, b) {
        // 准备输入数据
        _createBuffer([x, y])
        var sizeData = [size]
        // 加载着色器，准备读取缓冲区的数据
        _gl.useProgram(_pointShaderProgram)
        _gl.enableVertexAttribArray(_pointPositionAttributeLocation)
        // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
        // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
        // type=gl_FLOAT表示使用的是32为浮点类型的数据。
        // normalize=false表示不需要归一化数据。
        // offset=0表示从缓冲区的开始位置读取数据。
        _gl.vertexAttribPointer(_pointPositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

        _gl.uniform4f(_pointFragColor, _config.point_color.r, _config.point_color.g, _config.point_color.b, 1)

        _createBuffer(sizeData)
        _gl.vertexAttribPointer(_pointSizeAttributeLocation, 1, _gl.FLOAT, false, 0, 0)
        _gl.enableVertexAttribArray(_pointSizeAttributeLocation)

        _gl.drawArrays(_gl.POINTS, 0, 1)
    }

    /**
     * 绘制内存中保存的所有点
     */
    function _drawPoints() {
        // 准备输入数据
        _createBuffer(points)
        var sizeData = pointSizes
        // 加载着色器，准备读取缓冲区的数据
        _gl.useProgram(_pointShaderProgram)
        _gl.enableVertexAttribArray(_pointPositionAttributeLocation)
        // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
        // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
        // type=gl_FLOAT表示使用的是32为浮点类型的数据。
        // normalize=false表示不需要归一化数据。
        // offset=0表示从缓冲区的开始位置读取数据。
        _gl.vertexAttribPointer(_pointPositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

        _gl.uniform4f(_pointFragColor, _config.point_color.r, _config.point_color.g, _config.point_color.b, 1)

        _createBuffer(sizeData)
        _gl.vertexAttribPointer(_pointSizeAttributeLocation, 1, _gl.FLOAT, false, 0, 0)
        _gl.enableVertexAttribArray(_pointSizeAttributeLocation)

        _gl.drawArrays(_gl.POINTS, 0, points.length / 2)
    }

    /**
     * 绘制线段组
     * @param {*} points
     */
    function _drawLineGroup(points) {
        _createBuffer(points)
        _gl.useProgram(_lineShaderProgram)
        _gl.enableVertexAttribArray(_linePositionAttributeLocation)
        // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
        // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
        // type=gl_FLOAT表示使用的是32为浮点类型的数据。
        // normalize=false表示不需要归一化数据。
        // offset=0表示从缓冲区的开始位置读取数据。
        _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

        _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)

        _gl.drawArrays(_gl.LINES, 0, points.length / 2)
    }

    /**
     * 绘制实心圆 (r 0~1)
     */
    function _drawCircle(x, y, banjing, r, g, b, v) {
        var N = 1000
        var vertexData = [x, y]

        for (var i = 0; i <= N; i++) {
            var theta = i * 2 * Math.PI / N
            var x1 = x + banjing * Math.sin(theta)
            var y1 = y + banjing * Math.cos(theta)
            vertexData.push(x1, y1)
        }
        //console.log(vertexData)
        var vertices = new Float32Array(vertexData)
        _createBuffer(vertexData)
        // 加载着色器，准备读取缓冲区的数据
        _gl.useProgram(_lineShaderProgram)
        _gl.enableVertexAttribArray(_linePositionAttributeLocation)
        // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
        // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
        // type=gl_FLOAT表示使用的是32为浮点类型的数据。
        // normalize=false表示不需要归一化数据。
        // offset=0表示从缓冲区的开始位置读取数据。
        _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

        _gl.uniform4f(_lineFragColor, r, g, b, v)

        _gl.drawArrays(_gl.TRIANGLE_FAN, 0, vertices.length / 2)
    }

    /**
     * 绘制圆圈
     * @param {*} vertexData 圆圈上的点
     */
    function _drawHollowCircleWithPoints(vertexData) {
        _createBuffer(vertexData)
        // 加载着色器，准备读取缓冲区的数据
        _gl.useProgram(_lineShaderProgram)
        _gl.enableVertexAttribArray(_linePositionAttributeLocation)
        // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
        // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
        // type=gl_FLOAT表示使用的是32为浮点类型的数据。
        // normalize=false表示不需要归一化数据。
        // offset=0表示从缓冲区的开始位置读取数据。
        _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

        _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)

        _gl.drawArrays(_gl.LINE_LOOP, 0, vertexData.length / 2)
    }

    /**
     * 绘制圆圈
     * @param {*} x 原点x坐标
     * @param {*} y 原点y坐标
     * @param {*} pointSize 线条粗细
     * @param {*} banjing 半径
     * @param {*} r 颜色r值
     * @param {*} g 颜色g值
     * @param {*} b 颜色b值
     * @param {*} v
     */
    function _drawHollowCircle(x, y, pointSize, banjing, r, g, b, v) {
        var N = 2000
        var vertexData = []
        // var sizeData = [pointSize, pointSize]

        for (var i = 0; i <= N; i++) {
            var theta = i * 2 * Math.PI / N
            var x1 = x + banjing * Math.sin(theta)
            var y1 = y + banjing * Math.cos(theta)
            vertexData.push(x1, y1)
            //   sizeData.push(pointSize)
        }
        // //console.log(vertexData)
        _drawHollowCircleWithPoints(vertexData)
        return vertexData
    }

    // 准备WebGL上下文
    function _prepareContext() {
        // 使用Canvas
        let main_canvas = document.querySelector('#' + _config.container)
        // main_canvas.setAttribute('width', '600')
        // main_canvas.setAttribute('height', '600')
        _gl = main_canvas.getContext('webgl')
        if (!_gl) {
            //console.log('无法使用WebGL！')
        } else {
            // //console.log(_gl)
        }
    }
    // 编译
    function _setupShader(gl, type, source) {
        let shader = gl.createShader(type)
        gl.shaderSource(shader, source)
        gl.compileShader(shader)

        let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
        if (success) {
            return shader
        }

        //console.log(gl.getShaderInfoLog(shader))
        gl.deleteShader(shader)
    }
    // 创建缓冲区，并填入顶点数据
    function _createBuffer(data) {
        _gl.bindBuffer(_gl.ARRAY_BUFFER, _gl.createBuffer())
        _gl.bufferData(_gl.ARRAY_BUFFER, new Float32Array(data), _gl.STATIC_DRAW)
    }

    // 链接
    function linkShader(gl, vertexShader, fragmentShader) {
        let program = gl.createProgram()
        gl.attachShader(program, vertexShader)
        gl.attachShader(program, fragmentShader)
        gl.linkProgram(program)
        let success = gl.getProgramParameter(program, gl.LINK_STATUS)
        if (success) {
            return program
        }

        //console.log(gl.getProgramInfoLog(program))
        gl.deleteProgram(program)
    }

    /**
     * 全部涂黑
     */
    function _clear() {
        _gl.clearColor(_config.r, _config.g, _config.b, 1)
        _gl.clear(_gl.COLOR_BUFFER_BIT)
    }
    /**
     * 清空整个区域到原始状态
     */
    function _clearArea() {
        _clear()
        if (_config.type === 'xingzuo') {
            if (xingzuoCirclePoints1.length === 0) {
                xingzuoCirclePoints1 = _drawHollowCircle(0, 0, 1, 0.5, 1, 1, 1)
            } else {
                _drawHollowCircleWithPoints(xingzuoCirclePoints1)
            }
            if (xingzuoCirclePoints2.length === 0) {
                xingzuoCirclePoints2 = _drawHollowCircle(0, 0, 1, 1, 1, 1, 1)
            } else {
                _drawHollowCircleWithPoints(xingzuoCirclePoints2)
            }
            _drawCrossLine()
            _drawLine(1, 1, -1, -1)
            _drawLine(1, -1, -1, 1)
            _drawLine(-1, 0.5, 1, 0.5)
            _drawLine(-1, 0, 1, 0)
            _drawLine(-1, -0.5, 1, -0.5)
            _drawLine(0.5, 1, 0.5, -1)
            _drawLine(-0.5, 1, -0.5, -1)
        }
        if (_config.type === 'eyes') {
            _clear()
        }
    }

    /**
     * 转换坐标成webgl内部坐标
     * @param {*} x
     * @param {*} y
     */
    function _coordinateTransform(x, y) {
        var newX = x
        var newY = y
        if (_config.xAxisInterval) {
            var xpercent = (x - _config.xAxisInterval[0]) / (_config.xAxisInterval[1] - _config.xAxisInterval[0])
            var transformedX = (1 - (-1)) * xpercent + (-1)
            newX = transformedX
        }
        if (_config.xAxisInterval) {
            var percent = (y - _config.yAxisInterval[0]) / (_config.yAxisInterval[1] - _config.yAxisInterval[0])
            var transformedY = (1 - (-1)) * percent + (-1)
            newY = transformedY
        }
        return [newX, newY]
    }

    return {
        /**
         * 星座图
         * @param {} config
         */
        chartXingzuo(config) {
            // //console.log(config)
            _config = config
            _config.type = 'xingzuo'
            if (!_config.bg_color) {
                // 默认背景黑色
                _config.bg_color = {
                    r: 0,
                    g: 0,
                    b: 0
                }
            }
            if (!_config.line_color) {
                _config.line_color = {
                    r: 1,
                    g: 1,
                    b: 1
                }
            }
            if (!_config.point_color) {
                _config.point_color = {
                    r: 1,
                    g: 1,
                    b: 1
                }
            }
            _initGl()
            _clearArea()
        },
        /**
         * 眼图
         * @param {*} config
         */
        chartEyes(config) {
            _config = config
            _config.type = 'eyes'
            if (!_config.bg_color) {
                // 默认背景黑色
                _config.bg_color = {
                    r: 0,
                    g: 0,
                    b: 0
                }
            }
            if (!_config.line_color) {
                _config.line_color = {
                    r: 1,
                    g: 1,
                    b: 1
                }
            }
            if (!_config.point_color) {
                _config.point_color = {
                    r: 1,
                    g: 1,
                    b: 1
                }
            }
            _initGl()
            _clearArea()
        },
        /**
         * 添加一个点并立即绘制
         * @param {*} x
         * @param {*} y
         * @param {*} size
         */
        drawPoint(x, y, size) {
            points.push(x)
            points.push(y)
            pointSizes.push(size)
            _clearArea()
            _drawPoints()
        },
        /**
         * 添加多个点并立即绘制
         * @param {*} newPoints 格式[x1,y1,x2,y2,x3,y3]的数组
         * @param {*} size
         */
        drawPoints(newPoints, size) {
            for (var i = 0; i < newPoints.length; i++) {
                points.push(newPoints[i])
                pointSizes.push(size)
            }
            _clearArea()
            _drawPoints()
        },
        /**
         * 清空数据,刷新图像
         */
        clear() {
            _clearArea()
            points = []
            pointSizes = []
            linePoints = []
        },
        /**
         * 刷新图像
         */
        refresh() {
            _clearArea()
            _drawPoints()
            _drawLineGroup(linePoints)
        },
        /**
         * 添加一条线
         * @param {} newLinelinePoints 线上面的点的坐标数组
         */
        drawLine(newLinelinePoints) {
            var tmp = [...newLinelinePoints]
            for (var i = 0; i < tmp.length; i = i + 2) {
                var transformedXY = _coordinateTransform(newLinelinePoints[i], newLinelinePoints[i + 1])
                tmp[i] = transformedXY[0]
                tmp[i + 1] = transformedXY[1]
            }
            for (var j = 0; j < tmp.length; j++) {
                if (j > 2 && j % 2 === 0) {
                    linePoints.push(tmp[j - 2])
                    linePoints.push(tmp[j - 1])
                }
                linePoints.push(tmp[j])
            }

            _clearArea()
            _drawLineGroup(linePoints)
        },
        /**
         * 添加多条线
         * @param {*} newLinelinePoints 所有线上面点的坐标数组
         * @param {*} pointNum  每条线含有的点的数量
         */
        drawLines(newLinelinePoints, pointNum) {
            var tmp = [...newLinelinePoints]
            for (var i = 0; i < tmp.length; i = i + 2) {
                var transformedXY = _coordinateTransform(newLinelinePoints[i], newLinelinePoints[i + 1])
                tmp[i] = transformedXY[0]
                tmp[i + 1] = transformedXY[1]
            }
            for (var j = 0; j < tmp.length; j++) {
                if (j % (pointNum * 2) !== 0 && j % (pointNum * 2) > 2 && j % 2 === 0) {
                    linePoints.push(tmp[j - 2])
                    linePoints.push(tmp[j - 1])
                }
                linePoints.push(tmp[j])
            }
            _clearArea()
            _drawLineGroup(linePoints)
        }
    }
}
export default Chart
